import { ref, reactive, watch } from "vue";
import { AuthEnum, ResultEnum } from "@/enums/httpEnum";
import { useGlobSetting } from "@/hooks/setting";
import { useUserStore } from "@/store/modules/user";
import { computedMd5ByFile } from "@/utils/file";

const setting = useGlobSetting()
const userStore = useUserStore()

export function fileLoadHelper(getCurrentDirId: Function, reloadTable: Function) {

  const selectFileBtnRef = ref()
  const uploadBtnRef = ref()
  const downloadBtnRef = ref()
  const timeoutRef = ref<undefined | number>(undefined)

  const options = reactive({
    // 上传URL
    // @ts-ignore
    target: (file, chunk, isTest) => {
      let url = `${setting.urlPrefix}/file/upload`
      let dirId = getCurrentDirId()
      return `${url}/${dirId}`
    },
    // 单文件上传?
    singleFile: false,
    // 分块时按照该值来分 10M  10 * 1024 * 1024
    chunkSize: 10 * 1024 * 1024,
    // 上传并发数(并发上传线程数)
    simultaneousUploads: 3,
    // 上传文件时文件的参数名
    fileParameterName: 'file',
    // 额外的一些请求头
    headers: {
      [AuthEnum.AUTH_TOKEN_HEADER]: `${AuthEnum.AUTH_TOKEN_SCHEME} ${userStore.getToken}`
    },
    // 当上传的时候所使用的是方式
    method: 'multipart',
    // 测试的时候使用的 HTTP 方法
    testMethod: 'GET',
    // 真正上传的时候使用的 HTTP 方法
    uploadMethod: 'POST',
    // 是否测试每个块是否在服务端已经上传了，主要用来实现秒传、跨浏览器上传等
    testChunks: true,
    // 最大自动失败重试上传次数
    maxChunkRetries: 0,
    // 服务器分片校验函数，秒传及断点续传基础
    checkChunkUploadedByResponse: (chunk, result) => {
      const message = window['$message']
      const dialog = window['$dialog']
      let { code, message: msg, data } = JSON.parse(result);
      if (!code.toString().startsWith('2')) {
        // 登录超时就去登录页面
        if (code === ResultEnum.LOGIN_FAILURE) {
          dialog.warning({
            title: '提示',
            content: '登录身份已失效，请重新登录!',
            positiveText: '确定',
            closable: false,
            maskClosable: false,
            onPositiveClick: async () => {
              await userStore.logout();
            },
            onNegativeClick: () => {},
          });
          throw new Error(msg)
        }
        const uploader = chunk.uploader
        uploader.removeFile(chunk.file)
        message.error(msg)
        throw new Error(msg)
      }
      if (data.uploaded) {
        // 已上传
        return true;
      }
      // [1,3,4] --> 只需要上传分片2
      return (data.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0
    },
    // 修改文件唯一标识, 在 添加文件上传的回调函数之前完成, 无法异步, 替换之
    /*generateUniqueIdentifier: file => generator(file),*/
    // 处理请求参数
    // @ts-ignore
    processParams: (params, file, chunk, isTest) => {
      return isTest ? {
        ...params,
        name: file.name,
        type: file.type || 'application/octet-stream'
      } : params
    }
  })

  // @ts-ignore
  const onFilesAdded = (files, fileList, event) => {
    // 过滤本次所选的文件们
    files.ignored = true
    const message = window['$message'];
    // 没有登录
    if (!userStore.token) {
      message.warning('请先登录才能进行文件上传哦')
      return false
    }
    /*
        files -> 拖拽(选择)的所有文件
        fileList -> 拖拽(选择)的文件和文件夹(文件夹单独占用一个数组)
     */
    if (files.length === 0 || fileList === 0) {
      message.warning('空文件夹?')
      return false
    }
    // 如果长度不一样那么代表存在文件夹
    if (files.length !== fileList.length) {
      message.warning('不允许上传文件夹')
      return false
    }
    // 即使长度一样, 也有可能有存在快捷方式(.lnk), 或者是空目录
    for (let i = 0; i < fileList.length; i++) {
      let file = fileList[i]
      if (file.name.toString().endsWith(".lnk")) {
        message.warning('不允许上传(.lnk)未知类型')
        return false
      }
      if (file.isFolder) {
        message.warning('不允许上传文件夹')
        return false
      }
    }
    // 如果都没有问题, 那么就不过滤文件们
    files.ignored = false
    // 对上传的文件进行 identifier 的重新计算
    for (const f of files) {
      // 注意哈, 需要将文件设置一个暂停, 否则后续 resume 的时候会按照文件顺序来进行上传
      // 就会导致如果数组中第一个文件比较大, md5 还未计算出来就被拉去请求
      f.pause()
      computedMd5ByFile(f.file).then(md5 => {
        f.uniqueIdentifier = md5
        f.resume()
      })
    }
    // 手动弹出文件上传列表
    uploadBtnRef.value.$el.click()
    return true
  }

  // @ts-ignore
  const onFileSuccess = (rootFile, file, response, chunk) => {
    const message = window['$message'];
    const uploader = rootFile.uploader
    // 这里可以做一些上传成功之后的事情，比如，如果后端需要合并的话，可以通知到后端合并
    const { data, code, message: msg } = JSON.parse(response)
    // 这里也有可能出现错误
    if (code === ResultEnum.USER_ERROR) {
      message.warning(`${file.name}: ${msg}`)
      let currentFile = uploader.getFromUniqueIdentifier(file.uniqueIdentifier)
      currentFile.cancel()
      return;
    }
    if (data?.existed) {
      message.warning(`文件[${file.name}]已存在`)
      let currentFile = uploader.getFromUniqueIdentifier(file.uniqueIdentifier)
      currentFile.cancel()
    } else {
      message.info(`文件[${file.name}]上传成功`)
      uploader.removeFile(file)
    }
    // 刷新文件列表
    reloadTableTimeout(300)
  }

  /** 刷新数据, 指定时间内只执行一次 */
  const reloadTableTimeout = (timeout: number) => {
    if (timeoutRef.value) {
      window.clearTimeout(timeoutRef.value);
      timeoutRef.value = undefined;
    }
    timeoutRef.value = window.setTimeout(() => reloadTable(), timeout)
  }

  // @ts-ignore
  const onFileError = (rootFile, file, msg, chunk) => {
    const message = window['$message'];
    message.error(msg)
  }

  // @ts-ignore
  const onFileProgress = (rootFile, file, chunk) => { }

  // 用户token监听
  watch(() => userStore.token, token => {
    options.headers[AuthEnum.AUTH_TOKEN_HEADER] = `${AuthEnum.AUTH_TOKEN_SCHEME} ${token}`
  }, { immediate: true })

  return {
    options,
    uploadBtnRef, downloadBtnRef, selectFileBtnRef,
    onFilesAdded,
    onFileSuccess,
    onFileError,
    onFileProgress
  }
}
